{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2U8rtKk4EI3t"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "2zIDLJHABcjv"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RgNyvg23XJ7p"
      },
      "source": [
        "# 組み込みメソッドを使用したトレーニングと評価"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oxqkOC6o2vgF"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/keras/train_and_evaluate\">     <img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/guide/keras/train_and_evaluate.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"> Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/keras/train_and_evaluate.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/guide/keras/train_and_evaluate.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "teOcJ6hnSS5o"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mGYBLPAR13g3"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "from tensorflow.keras import layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "289cZbiRoKUa"
      },
      "source": [
        "## はじめに\n",
        "\n",
        "このガイドでは、トレーニングと検証に組み込みAPI ( `model.fit()`、`model.evaluate()`、`model.predict()`など) を使用する場合のトレーニング、評価、予測 (推論) モデルについて説明します。\n",
        "\n",
        "独自のトレーニングステップ関数を指定するときに`fit()`を活用することに関心がある場合は、<a data-md-type=\"link\" href=\"https://www.tensorflow.org/guide/keras/customizing_what_happens_in_fit/\">「`fit()`で行われる処理のカスタマイズ」</a>をご覧ください 。\n",
        "\n",
        "独自のトレーニングと評価ループを新規作成することに関心がある場合は、[「トレーニングループの新規作成」](https://www.tensorflow.org/guide/keras/writing_a_training_loop_from_scratch/)をご覧ください。\n",
        "\n",
        "一般的に、組み込みループを使用している場合でも、独自のループを作成している場合でも、モデルのトレーニングと評価は、Sequential モデル、Functional API を使用して構築されたモデル、モデルのサブクラス化により新規作成されたモデルなど、あらゆるタイプの Keras モデルで同じように機能します。\n",
        "\n",
        "このガイドは、分散トレーニングについては取り上げていません。 分散トレーニングについては、[「マルチ GPU と分散トレーニングのガイド」](/guides/distributed_training/)を参照してください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wJ51FHNdmeHu"
      },
      "source": [
        "## API の概要：最初のエンドツーエンドの例\n",
        "\n",
        "モデルの組み込みトレーニングループにデータを渡すときは、**NumPy 配列**（データが小さく、メモリに収まる場合）または**`tf.data Dataset`オブジェクト**を使用する必要があります。 以下の例では、オプティマイザ、損失、およびメトリックの使用方法を示すために、MNIST データセットを NumPy 配列として使用します。\n",
        "\n",
        "次のモデルを見てみましょう（ここでは、Functional API を使用して構築していますが、Sequential モデルまたはサブクラスモデルを使用する場合もあります）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1GivII8YruI1"
      },
      "outputs": [],
      "source": [
        "inputs = keras.Input(shape=(784,), name=\"digits\")\n",
        "x = layers.Dense(64, activation=\"relu\", name=\"dense_1\")(inputs)\n",
        "x = layers.Dense(64, activation=\"relu\", name=\"dense_2\")(x)\n",
        "outputs = layers.Dense(10, activation=\"softmax\", name=\"predictions\")(x)\n",
        "\n",
        "model = keras.Model(inputs=inputs, outputs=outputs)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Dq8gCsh8cc8m"
      },
      "source": [
        "一般的なエンドツーエンドのワークフローは以下のとおりです。\n",
        "\n",
        "- トレーニング\n",
        "- 元のトレーニングデータから生成されたホールドアウトセットの検証\n",
        "- テストデータの評価\n",
        "\n",
        "この例では MNIST データを使用します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WTrVrf4QiojU"
      },
      "outputs": [],
      "source": [
        "(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()\n",
        "\n",
        "# Preprocess the data (these are NumPy arrays)\n",
        "x_train = x_train.reshape(60000, 784).astype(\"float32\") / 255\n",
        "x_test = x_test.reshape(10000, 784).astype(\"float32\") / 255\n",
        "\n",
        "y_train = y_train.astype(\"float32\")\n",
        "y_test = y_test.astype(\"float32\")\n",
        "\n",
        "# Reserve 10,000 samples for validation\n",
        "x_val = x_train[-10000:]\n",
        "y_val = y_train[-10000:]\n",
        "x_train = x_train[:-10000]\n",
        "y_train = y_train[:-10000]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4kgHixUOrWek"
      },
      "source": [
        "トレーニングの構成（オプティマイザ、損失、メトリック）を指定します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Or9HAqBbBS5m"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(),  # Optimizer\n",
        "    # Loss function to minimize\n",
        "    loss=keras.losses.SparseCategoricalCrossentropy(),\n",
        "    # List of metrics to monitor\n",
        "    metrics=[keras.metrics.SparseCategoricalAccuracy()],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "36rFtS30q56S"
      },
      "source": [
        "`fit()`を呼び出します。これは、データを「batch_size」サイズの「バッチ」にスライスし、指定された数の「エポック」間にデータセット全体を繰り返し反復することによりモデルをトレーニングします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yhNJ3Hq30luG"
      },
      "outputs": [],
      "source": [
        "print(\"Fit model on training data\")\n",
        "history = model.fit(\n",
        "    x_train,\n",
        "    y_train,\n",
        "    batch_size=64,\n",
        "    epochs=2,\n",
        "    # We pass some validation for\n",
        "    # monitoring validation loss and metrics\n",
        "    # at the end of each epoch\n",
        "    validation_data=(x_val, y_val),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Hv6F201DiBVr"
      },
      "source": [
        "返された「履歴」オブジェクトは、トレーニング間の損失値とメトリック値の記録を保持します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CRP8KiGsxYdO"
      },
      "outputs": [],
      "source": [
        "history.history"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LNuFWerJNjWt"
      },
      "source": [
        "`evaluate()`を介してテストデータのモデルを評価します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1tX8Yk2ZryVJ"
      },
      "outputs": [],
      "source": [
        "# Evaluate the model on the test data using `evaluate`\n",
        "print(\"Evaluate on test data\")\n",
        "results = model.evaluate(x_test, y_test, batch_size=128)\n",
        "print(\"test loss, test acc:\", results)\n",
        "\n",
        "# Generate predictions (probabilities -- the output of the last layer)\n",
        "# on new data using `predict`\n",
        "print(\"Generate predictions for 3 samples\")\n",
        "predictions = model.predict(x_test[:3])\n",
        "print(\"predictions shape:\", predictions.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ul1Ovpw2uIMa"
      },
      "source": [
        "では、このワークフローの各部分を詳しく見ていきましょう。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WxC3o3XOHv7O"
      },
      "source": [
        "## `compile()`メソッド：損失、メトリック、およびオプティマイザを指定する\n",
        "\n",
        "`fit()`を使用してモデルをトレーニングするには、損失関数、オプティマイザ、および必要に応じて監視するいくつかのメトリックを指定する必要があります。\n",
        "\n",
        "これらを`compile()`メソッドの引数としてモデルに渡します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ELAoiVowq4nj"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),\n",
        "    loss=keras.losses.SparseCategoricalCrossentropy(),\n",
        "    metrics=[keras.metrics.SparseCategoricalAccuracy()],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RtRJ3EQzQXdq"
      },
      "source": [
        "`metrics`引数はリストである必要があります。モデルは任意の数のメトリックを持つことができます。\n",
        "\n",
        "モデルに複数の出力がある場合、出力ごとに異なる損失とメトリックを指定でき、モデルの総損失に対する各出力の寄与を変調できます。詳細については、**「データを多入力多出力モデルに渡す」**セクションを参照してください。\n",
        "\n",
        "デフォルト設定に満足している場合、多くの場合、オプティマイザ、損失、およびメトリックは、ショートカットとして文字列識別子を介して指定できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OsRubkHuXh4u"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=\"rmsprop\",\n",
        "    loss=\"sparse_categorical_crossentropy\",\n",
        "    metrics=[\"sparse_categorical_accuracy\"],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TdOjf67OGnZF"
      },
      "source": [
        "後で再利用するために、モデルの定義とコンパイルの手順を関数に入れます。本ガイドでは、これらをさまざまな例で数回呼び出します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mT62JxAkonpb"
      },
      "outputs": [],
      "source": [
        "def get_uncompiled_model():\n",
        "    inputs = keras.Input(shape=(784,), name=\"digits\")\n",
        "    x = layers.Dense(64, activation=\"relu\", name=\"dense_1\")(inputs)\n",
        "    x = layers.Dense(64, activation=\"relu\", name=\"dense_2\")(x)\n",
        "    outputs = layers.Dense(10, activation=\"softmax\", name=\"predictions\")(x)\n",
        "    model = keras.Model(inputs=inputs, outputs=outputs)\n",
        "    return model\n",
        "\n",
        "\n",
        "def get_compiled_model():\n",
        "    model = get_uncompiled_model()\n",
        "    model.compile(\n",
        "        optimizer=\"rmsprop\",\n",
        "        loss=\"sparse_categorical_crossentropy\",\n",
        "        metrics=[\"sparse_categorical_accuracy\"],\n",
        "    )\n",
        "    return model\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8KIw3lKFY4v3"
      },
      "source": [
        "### 数多くの組み込みオプティマイザ、損失とメトリクスが利用可能\n",
        "\n",
        "多くの場合、Keras API には、必要とされるものは含まれているので、一般的に、損失、メトリック、オプティマイザを新規作成する必要はありません。\n",
        "\n",
        "オプティマイザ：\n",
        "\n",
        "- `SGD()`（モメンタム有り、モメンタム無し）\n",
        "- `RMSprop()`\n",
        "- `Adam()`\n",
        "- その他\n",
        "\n",
        "損失：\n",
        "\n",
        "- `MeanSquaredError()`\n",
        "- `KLDivergence()`\n",
        "- `CosineSimilarity()`\n",
        "- その他\n",
        "\n",
        "メトリック：\n",
        "\n",
        "- `AUC()`\n",
        "- `Precision()`\n",
        "- `Recall()`\n",
        "- その他"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hTth4Ksz1cDr"
      },
      "source": [
        "### カスタム損失\n",
        "\n",
        "Keras でカスタム損失を提供する方法は 2 つあります。 最初の例では、入力`y_true`および`y_pred`を受け入れる関数を作成します。2 つめの例は、実データと予測の間の平均二乗誤差を計算する損失関数を示しています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EAuZtRoViEtN"
      },
      "outputs": [],
      "source": [
        "def custom_mean_squared_error(y_true, y_pred):\n",
        "    return tf.math.reduce_mean(tf.square(y_true - y_pred))\n",
        "\n",
        "\n",
        "model = get_uncompiled_model()\n",
        "model.compile(optimizer=keras.optimizers.Adam(), loss=custom_mean_squared_error)\n",
        "\n",
        "# We need to one-hot encode the labels to use MSE\n",
        "y_train_one_hot = tf.one_hot(y_train, depth=10)\n",
        "model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BHbC8v5AYTUy"
      },
      "source": [
        "`y_true`および`y_pred`以外のパラメータを受け取る損失関数が必要な場合は、`tf.keras.losses.Loss`クラスをサブクラス化して、次の 2 つのメソッドを実装できます。\n",
        "\n",
        "- `__init__(self)`: 損失関数の呼び出し中に渡すパラメータを受け入れる\n",
        "- `call(self, y_true, y_pred)`: ターゲット（y_true）とモデル予測（y_pred）を使用してモデルの損失を計算する\n",
        "\n",
        "平均二乗誤差を使用する際に、0.5 より離れた予測値にペナルティを与えるとします（カテゴリターゲットはワンホットエンコードされ、0 と 1 の間の値を取ると想定します）。これにより、モデルの信頼性が調整され、過剰適合を防ぐのに役立ちます（実際に試してみるまで、その効果はわかりませんが）。\n",
        "\n",
        "以下のように記述します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wEOeF6UqgEvG"
      },
      "outputs": [],
      "source": [
        "class CustomMSE(keras.losses.Loss):\n",
        "    def __init__(self, regularization_factor=0.1, name=\"custom_mse\"):\n",
        "        super().__init__(name=name)\n",
        "        self.regularization_factor = regularization_factor\n",
        "\n",
        "    def call(self, y_true, y_pred):\n",
        "        mse = tf.math.reduce_mean(tf.square(y_true - y_pred))\n",
        "        reg = tf.math.reduce_mean(tf.square(0.5 - y_pred))\n",
        "        return mse + reg * self.regularization_factor\n",
        "\n",
        "\n",
        "model = get_uncompiled_model()\n",
        "model.compile(optimizer=keras.optimizers.Adam(), loss=CustomMSE())\n",
        "\n",
        "y_train_one_hot = tf.one_hot(y_train, depth=10)\n",
        "model.fit(x_train, y_train_one_hot, batch_size=64, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wdoCA46g3HPg"
      },
      "source": [
        "### カスタムメトリック\n",
        "\n",
        "API に含まれていないメトリックが必要な場合は、`tf.keras.metrics.Metric`クラスをサブクラス化することにより、カスタムメトリックを簡単に作成できます。\n",
        "\n",
        "- `__init__(self)` - メトリックの状態変数を作成します。\n",
        "- `update_state(self, y_true, y_pred, sample_weight=None)` - ターゲット (y_true) とモデル予測 (y_pred) を使用し状態変数を更新します。\n",
        "- `result(self)` - 状態変数を使用して最終結果を計算します。\n",
        "- `reset_states(self)` - メトリックの状態を再初期化します。\n",
        "\n",
        "多くの場合、結果の計算に非常に負荷がかかり、定期的にしか実行されないため、状態の更新（`update_state()`）と結果の計算（`result()`）は別々に保持されます。\n",
        "\n",
        "以下は、`CategoricalTruePositives`メトリックを実装する方法を示す簡単な例です。これは、特定のクラスに属するものとして正しく分類されたサンプル数を数えます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f6hNnE0Xz945"
      },
      "outputs": [],
      "source": [
        "class CategoricalTruePositives(keras.metrics.Metric):\n",
        "    def __init__(self, name=\"categorical_true_positives\", **kwargs):\n",
        "        super(CategoricalTruePositives, self).__init__(name=name, **kwargs)\n",
        "        self.true_positives = self.add_weight(name=\"ctp\", initializer=\"zeros\")\n",
        "\n",
        "    def update_state(self, y_true, y_pred, sample_weight=None):\n",
        "        y_pred = tf.reshape(tf.argmax(y_pred, axis=1), shape=(-1, 1))\n",
        "        values = tf.cast(y_true, \"int32\") == tf.cast(y_pred, \"int32\")\n",
        "        values = tf.cast(values, \"float32\")\n",
        "        if sample_weight is not None:\n",
        "            sample_weight = tf.cast(sample_weight, \"float32\")\n",
        "            values = tf.multiply(values, sample_weight)\n",
        "        self.true_positives.assign_add(tf.reduce_sum(values))\n",
        "\n",
        "    def result(self):\n",
        "        return self.true_positives\n",
        "\n",
        "    def reset_states(self):\n",
        "        # The state of the metric will be reset at the start of each epoch.\n",
        "        self.true_positives.assign(0.0)\n",
        "\n",
        "\n",
        "model = get_uncompiled_model()\n",
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),\n",
        "    loss=keras.losses.SparseCategoricalCrossentropy(),\n",
        "    metrics=[CategoricalTruePositives()],\n",
        ")\n",
        "model.fit(x_train, y_train, batch_size=64, epochs=3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XSC3upuYsZJf"
      },
      "source": [
        "### 標準のシグネチャに適合しない損失と測定基準の処理\n",
        "\n",
        "ほとんどの損失とメトリックは、`y_true`および`y_pred`から計算できます。この場合、`y_pred`はモデルの出力です。例外として、正則化損失ではレイヤーのアクティブ化のみが必要で（この場合はターゲットはありません）、このアクティブ化はモデルの出力ではない場合があります。\n",
        "\n",
        "このような場合は、カスタムレイヤーの呼び出しメソッド内から`self.add_loss(loss_value)`を呼び出すことができます。この方法で追加された損失は、トレーニング時に「メイン」の損失（`compile()`に渡されたもの）に追加されます。以下は、アクティビティの正規化を追加する簡単な例です（アクティビティの正規化はすべての Keras レイヤーに組み込まれています。このレイヤーは具体的な例を示すためのものです）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uEoMY1baigwm"
      },
      "outputs": [],
      "source": [
        "class ActivityRegularizationLayer(layers.Layer):\n",
        "    def call(self, inputs):\n",
        "        self.add_loss(tf.reduce_sum(inputs) * 0.1)\n",
        "        return inputs  # Pass-through layer.\n",
        "\n",
        "\n",
        "inputs = keras.Input(shape=(784,), name=\"digits\")\n",
        "x = layers.Dense(64, activation=\"relu\", name=\"dense_1\")(inputs)\n",
        "\n",
        "# Insert activity regularization as a layer\n",
        "x = ActivityRegularizationLayer()(x)\n",
        "\n",
        "x = layers.Dense(64, activation=\"relu\", name=\"dense_2\")(x)\n",
        "outputs = layers.Dense(10, name=\"predictions\")(x)\n",
        "\n",
        "model = keras.Model(inputs=inputs, outputs=outputs)\n",
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),\n",
        "    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        ")\n",
        "\n",
        "# The displayed loss will be much higher than before\n",
        "# due to the regularization component.\n",
        "model.fit(x_train, y_train, batch_size=64, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LOrHwOqG5z0I"
      },
      "source": [
        "`add_metric()`を使用して、メトリック値のロギングに対して同じく実行できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jJlqaoWBItbV"
      },
      "outputs": [],
      "source": [
        "class MetricLoggingLayer(layers.Layer):\n",
        "    def call(self, inputs):\n",
        "        # The `aggregation` argument defines\n",
        "        # how to aggregate the per-batch values\n",
        "        # over each epoch:\n",
        "        # in this case we simply average them.\n",
        "        self.add_metric(\n",
        "            keras.backend.std(inputs), name=\"std_of_activation\", aggregation=\"mean\"\n",
        "        )\n",
        "        return inputs  # Pass-through layer.\n",
        "\n",
        "\n",
        "inputs = keras.Input(shape=(784,), name=\"digits\")\n",
        "x = layers.Dense(64, activation=\"relu\", name=\"dense_1\")(inputs)\n",
        "\n",
        "# Insert std logging as a layer.\n",
        "x = MetricLoggingLayer()(x)\n",
        "\n",
        "x = layers.Dense(64, activation=\"relu\", name=\"dense_2\")(x)\n",
        "outputs = layers.Dense(10, name=\"predictions\")(x)\n",
        "\n",
        "model = keras.Model(inputs=inputs, outputs=outputs)\n",
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(learning_rate=1e-3),\n",
        "    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        ")\n",
        "model.fit(x_train, y_train, batch_size=64, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8f7FBpwJOJAS"
      },
      "source": [
        "[Functional API](https://www.tensorflow.org/guide/keras/functional/) では、`model.add_loss(loss_tensor)`または`model.add_metric(metric_tensor, name, aggregation)`を呼び出せます。\n",
        "\n",
        "簡単な例を下記に示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "voJumeU4louI"
      },
      "outputs": [],
      "source": [
        "inputs = keras.Input(shape=(784,), name=\"digits\")\n",
        "x1 = layers.Dense(64, activation=\"relu\", name=\"dense_1\")(inputs)\n",
        "x2 = layers.Dense(64, activation=\"relu\", name=\"dense_2\")(x1)\n",
        "outputs = layers.Dense(10, name=\"predictions\")(x2)\n",
        "model = keras.Model(inputs=inputs, outputs=outputs)\n",
        "\n",
        "model.add_loss(tf.reduce_sum(x1) * 0.1)\n",
        "\n",
        "model.add_metric(keras.backend.std(x1), name=\"std_of_activation\", aggregation=\"mean\")\n",
        "\n",
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        ")\n",
        "model.fit(x_train, y_train, batch_size=64, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hIg9AQ8fiku4"
      },
      "source": [
        "`add_loss()`を介して損失を渡すと、モデルにはすでに最小化する損失があるため、損失関数なしで`compile()`を呼び出すことが可能になります。\n",
        "\n",
        "次の`LogisticEndpoint`レイヤーでは、入力としてターゲットとロジットを取り、`add_loss()`を介してクロスエントロピー損失を追跡します。また、`add_metric()`を介して分類の精度を追跡します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "cmpAV3nY6WRR"
      },
      "outputs": [],
      "source": [
        "class LogisticEndpoint(keras.layers.Layer):\n",
        "    def __init__(self, name=None):\n",
        "        super(LogisticEndpoint, self).__init__(name=name)\n",
        "        self.loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)\n",
        "        self.accuracy_fn = keras.metrics.BinaryAccuracy()\n",
        "\n",
        "    def call(self, targets, logits, sample_weights=None):\n",
        "        # Compute the training-time loss value and add it\n",
        "        # to the layer using `self.add_loss()`.\n",
        "        loss = self.loss_fn(targets, logits, sample_weights)\n",
        "        self.add_loss(loss)\n",
        "\n",
        "        # Log accuracy as a metric and add it\n",
        "        # to the layer using `self.add_metric()`.\n",
        "        acc = self.accuracy_fn(targets, logits, sample_weights)\n",
        "        self.add_metric(acc, name=\"accuracy\")\n",
        "\n",
        "        # Return the inference-time prediction tensor (for `.predict()`).\n",
        "        return tf.nn.softmax(logits)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yhpJo97oNuVx"
      },
      "source": [
        "次のように、`loss`引数なしでコンパイルされた 2 つの入力（入力データとターゲット）を持つモデルで使用できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tpYO0EuZf3c1"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "inputs = keras.Input(shape=(3,), name=\"inputs\")\n",
        "targets = keras.Input(shape=(10,), name=\"targets\")\n",
        "logits = keras.layers.Dense(10)(inputs)\n",
        "predictions = LogisticEndpoint(name=\"predictions\")(logits, targets)\n",
        "\n",
        "model = keras.Model(inputs=[inputs, targets], outputs=predictions)\n",
        "model.compile(optimizer=\"adam\")  # No loss argument!\n",
        "\n",
        "data = {\n",
        "    \"inputs\": np.random.random((3, 3)),\n",
        "    \"targets\": np.random.random((3, 10)),\n",
        "}\n",
        "model.fit(data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e9ATCfOObGXh"
      },
      "source": [
        "多入力モデルのトレーニングの詳細については、「**多入力多出力モデルへのデータの受け渡し**」をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OJ3XxUkOCJDA"
      },
      "source": [
        "### 自動的にホールドアウトセットを別にする\n",
        "\n",
        "最初に紹介したエンドツーエンドの例では、各エポックの終わりに検証損失と検証メトリックを評価するために、NumPy 配列のタプル`(x_val, y_val)`をモデルに渡すために`validation_data`引数を使用しました。\n",
        "\n",
        "もう一つのオプションとして、` validation_split `引数は、検証のためにトレーニングデータの一部を自動的に取っておくことを可能にします。 引数値は検証のために取っておかれるデータの割合を表し、 0 より高く 1 より低い数字に設定します。たとえば、`validation_split=0.2` は「検証のためにデータの 20% を使用する」ことを意味し、`validation_split=0.6`は「検証のためにデータの 60% を使用する」ことを意味します。\n",
        "\n",
        "任意のシャッフルの前に、fitの呼び出しにより受け取った配列の最後の x% サンプルを取り、検証が計算されます。\n",
        "\n",
        "NumPy データでトレーニングする場合は、`validation_split`のみを使用できることに注意してください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TraQan0uov0T"
      },
      "outputs": [],
      "source": [
        "model = get_compiled_model()\n",
        "model.fit(x_train, y_train, batch_size=64, validation_split=0.2, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "M4WbEioWqf2j"
      },
      "source": [
        "## tf.data データセットからのトレーニングと評価\n",
        "\n",
        "前の段落では、損失、メトリクスおよびオプティマイザをどのように扱うかを見ました。そしてデータが Numpy 配列として渡されるとき、fit で`validation_data`と`validation_split`引数をどのように使用するかを見ました。\n",
        "\n",
        "次に、データが`tf.data.Dataset`オブジェクトの形式で渡される場合を見てみましょう。\n",
        "\n",
        "`tf.data` API は高速でスケーラブルな方法でデータを読み込んで前処理するためのTensorFlow 2.0 の一連のユティリティです。\n",
        "\n",
        "`Datasets`の作成についての詳細は、[tf.data ドキュメント](https://www.tensorflow.org/guide/data)をご覧ください。\n",
        "\n",
        "`Dataset`のインスタンスは、`fit()`、`evaluate()`、`predict()`メソッドに直接的に渡すことができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "t213jBqAFwb5"
      },
      "outputs": [],
      "source": [
        "model = get_compiled_model()\n",
        "\n",
        "# First, let's create a training Dataset instance.\n",
        "# For the sake of our example, we'll use the same MNIST data as before.\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
        "# Shuffle and slice the dataset.\n",
        "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
        "\n",
        "# Now we get a test dataset.\n",
        "test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))\n",
        "test_dataset = test_dataset.batch(64)\n",
        "\n",
        "# Since the dataset already takes care of batching,\n",
        "# we don't pass a `batch_size` argument.\n",
        "model.fit(train_dataset, epochs=3)\n",
        "\n",
        "# You can also evaluate or predict on a dataset.\n",
        "print(\"Evaluate\")\n",
        "result = model.evaluate(test_dataset)\n",
        "dict(zip(model.metrics_names, result))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3f3Y65TiYoap"
      },
      "source": [
        "データセットは各エポックの最後にリセットされるため、次のエポックで再利用できます。\n",
        "\n",
        "このデータセットから特定の数のバッチでのみトレーニングを実行する場合は、`steps_per_epoch`引数を渡します。これは、次のエポックに進む前に、このデータセットを使用してモデルが実行するトレーニングステップの数を指定します。\n",
        "\n",
        "この場合、データセットは各エポックの終わりにリセットされず、次のバッチを取得し続けます。最終的にデータセットのデータは使い果たされます（無限ループのデータセットでない限り）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ycoSkTq0J5kR"
      },
      "outputs": [],
      "source": [
        "model = get_compiled_model()\n",
        "\n",
        "# Prepare the training dataset\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
        "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
        "\n",
        "# Only use the 100 batches per epoch (that's 64 * 100 samples)\n",
        "model.fit(train_dataset, epochs=3, steps_per_epoch=100)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7prZFnId4Xru"
      },
      "source": [
        "### 検証データセットの使用\n",
        "\n",
        "`Dataset`インスタンスを`fit()`の`validation_data`引数として渡すことができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ET87Q427EbNM"
      },
      "outputs": [],
      "source": [
        "model = get_compiled_model()\n",
        "\n",
        "# Prepare the training dataset\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
        "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
        "\n",
        "# Prepare the validation dataset\n",
        "val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))\n",
        "val_dataset = val_dataset.batch(64)\n",
        "\n",
        "model.fit(train_dataset, epochs=1, validation_data=val_dataset)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zCWuTgqZ6sPe"
      },
      "source": [
        "各エポックの終わりに、モデルは検証データセットを反復処理し、検証損失と検証メトリックを計算します。\n",
        "\n",
        "このデータセットから特定の数のバッチでのみ検証を実行する場合は、`validation_steps`引数を渡すことができます。これは、検証を中断して次のエポックに進む前に、モデルが検証データセットで実行する必要がある検証ステップの数を指定します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "esU1Cr1O9rOc"
      },
      "outputs": [],
      "source": [
        "model = get_compiled_model()\n",
        "\n",
        "# Prepare the training dataset\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
        "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
        "\n",
        "# Prepare the validation dataset\n",
        "val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))\n",
        "val_dataset = val_dataset.batch(64)\n",
        "\n",
        "model.fit(\n",
        "    train_dataset,\n",
        "    epochs=1,\n",
        "    # Only run validation using the first 10 batches of the dataset\n",
        "    # using the `validation_steps` argument\n",
        "    validation_data=val_dataset,\n",
        "    validation_steps=10,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "y4n3HmmQ1cSh"
      },
      "source": [
        "検証データセットは使用するたびにリセットされることに注意してください（エポックごとに常に同じサンプルが評価されます）。\n",
        "\n",
        "`validation_split`引数（トレーニングデータからホールドアウトセットを生成）は、`Dataset`オブジェクトからトレーニングする場合はサポートされません。この機能には、データセットのサンプルにインデックスを付ける機能が必要ですが、一般的に`Dataset` API では不可能です。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pOI6qq7qvl3S"
      },
      "source": [
        "## サポートされるほかの入力フォーマット\n",
        "\n",
        "NumPy 配列、eager tensors、TensorFlow `Datasets`の他、Pandas データフレームやデータとラベルのバッチを生成する Python ジェネレータを使用して Keras モデルをトレーニングできます。\n",
        "\n",
        "特に、`keras.utils.Sequence`クラスは、Pythonデータジェネレータを構築するためのシンプルなインターフェースを提供します。 これらはマルチプロセッシングに対応し、シャッフルすることができます。\n",
        "\n",
        "一般的に、以下を使用することをお勧めします。\n",
        "\n",
        "- NumPy 入力データ - データが小さく、メモリに収まる場合\n",
        "- `Dataset`オブジェクト - 大規模なデータセットがあり、分散トレーニングを行う必要がある場合\n",
        "- `Sequence`オブジェクト - 大規模なデータセットがあり、TensorFlow では実行できない多くのカスタム Python 側の処理を行う必要がある場合（たとえば、データの読み込みや前処理が外部ライブラリに依存している場合）。\n",
        "\n",
        "## `keras.utils.Sequence`オブジェクトを入力として使用\n",
        "\n",
        "`keras.utils.Sequence`は、以下の 2 つの重要なプロパティを持つ Python ジェネレータを取得するためにサブクラス化できるユーティリティです。\n",
        "\n",
        "- マルチプロセッシングで正しく機能する。\n",
        "- シャッフル可能（`shuffle=True`を`fit()`で渡す場合など）。\n",
        "\n",
        "`Sequence`は以下の 2 つのメソッドを実装する必要があります。\n",
        "\n",
        "- `__getitem__`\n",
        "- `__len__`\n",
        "\n",
        "`__getitem__`メソッドは完全なバッチを返す必要があります。エポック間でデータセットを変更する場合は、`on_epoch_end`を実装できます。\n",
        "\n",
        "簡単な例を次に示します。\n",
        "\n",
        "```python\n",
        "from skimage.io import imread\n",
        "from skimage.transform import resize\n",
        "import numpy as np\n",
        "\n",
        "# Here, `filenames` is list of path to the images\n",
        "# and `labels` are the associated labels.\n",
        "\n",
        "class CIFAR10Sequence(Sequence):\n",
        "    def __init__(self, filenames, labels, batch_size):\n",
        "        self.filenames, self.labels = filenames, labels\n",
        "        self.batch_size = batch_size\n",
        "\n",
        "    def __len__(self):\n",
        "        return int(np.ceil(len(self.filenames) / float(self.batch_size)))\n",
        "\n",
        "    def __getitem__(self, idx):\n",
        "        batch_x = self.filenames[idx * self.batch_size:(idx + 1) * self.batch_size]\n",
        "        batch_y = self.labels[idx * self.batch_size:(idx + 1) * self.batch_size]\n",
        "        return np.array([\n",
        "            resize(imread(filename), (200, 200))\n",
        "               for filename in batch_x]), np.array(batch_y)\n",
        "\n",
        "sequence = CIFAR10Sequence(filenames, labels, batch_size)\n",
        "model.fit(sequence, epochs=10)\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YTY1mCJ6F03i"
      },
      "source": [
        "## サンプルの重み付けとクラスの重み付けの使用\n",
        "\n",
        "デフォルト設定では、サンプルの重みはデータセット内の頻度によって決定されます。サンプルの頻度に関係なく、データに重みを付ける方法は2つあります。\n",
        "\n",
        "- クラスの重み\n",
        "- サンプルの重み"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fjggBDd7IzeZ"
      },
      "source": [
        "### クラスの重み\n",
        "\n",
        "クラスに対する重みは、ディクショナリを`class_weight`引数に渡し、`Model.fit()`に渡すことで設定されます。このディクショナリは、クラスインデックスを、このクラスに属するサンプルに使用する重みにマップします。\n",
        "\n",
        "これは、リサンプリングせずにクラスのバランスを取るために使用できます。または、特定のクラスをより重要視するモデルをトレーニングするために使用できます。\n",
        "\n",
        "たとえば、クラス「0」がデータでクラス「1」として表されるものの半分である場合、`Model.fit(..., class_weight={0: 1., 1: 0.5})`を使用できます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xGa65BdyLdy6"
      },
      "source": [
        "以下は NumPy の例です。クラスの重みまたはサンプルの重みを使用して、クラス＃5（MNIST データセットの数字「5」）の正しい分類をより重要視しています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oPBuPHqYIqum"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "class_weight = {\n",
        "    0: 1.0,\n",
        "    1: 1.0,\n",
        "    2: 1.0,\n",
        "    3: 1.0,\n",
        "    4: 1.0,\n",
        "    # Set weight \"2\" for class \"5\",\n",
        "    # making this class 2x more important\n",
        "    5: 2.0,\n",
        "    6: 1.0,\n",
        "    7: 1.0,\n",
        "    8: 1.0,\n",
        "    9: 1.0,\n",
        "}\n",
        "\n",
        "print(\"Fit with class weight\")\n",
        "model = get_compiled_model()\n",
        "model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DclU4GWLjuCN"
      },
      "source": [
        "### サンプルの重み\n",
        "\n",
        "きめ細かい制御を必要とする場合、または分類子を構築しない場合は、「サンプルの重み」を使用できます。\n",
        "\n",
        "- NumPy データからトレーニングする場合：`sample_weight`引数を`Model.fit()`に渡します。\n",
        "- `tf.data`またはその他のイテレータからトレーニングする場合：`(input_batch, label_batch, sample_weight_batch)`タプルを介します。\n",
        "\n",
        "「サンプルの重み」配列は、バッチ内の各サンプルが総損失を計算する際に必要な重みを指定する数値の配列です。 これは、不均衡な分類の問題（頻繁に使用されないクラスに、より大きな重みを与えるため）でよく使用されます。\n",
        "\n",
        "使用される重みが 1 と 0 の場合、配列は損失関数の*マスク*として使用できます（損失全体に対する特定のサンプルの寄与を完全に破棄します）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oceIUhLclV5i"
      },
      "outputs": [],
      "source": [
        "sample_weight = np.ones(shape=(len(y_train),))\n",
        "sample_weight[y_train == 5] = 2.0\n",
        "\n",
        "print(\"Fit with sample weight\")\n",
        "model = get_compiled_model()\n",
        "model.fit(x_train, y_train, sample_weight=sample_weight, batch_size=64, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Z3yZvJwG7hMo"
      },
      "source": [
        "以下は、一致する`Dataset`の例です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RC8WB7BVM2ty"
      },
      "outputs": [],
      "source": [
        "sample_weight = np.ones(shape=(len(y_train),))\n",
        "sample_weight[y_train == 5] = 2.0\n",
        "\n",
        "# Create a Dataset that includes sample weights\n",
        "# (3rd element in the return tuple).\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train, sample_weight))\n",
        "\n",
        "# Shuffle and slice the dataset.\n",
        "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
        "\n",
        "model = get_compiled_model()\n",
        "model.fit(train_dataset, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lp36RvM6jxrY"
      },
      "source": [
        "## マルチ入力、マルチ出力モデルにデータを渡す\n",
        "\n",
        "前の例では、１つの入力（テンソルの形状`(764,)`）と１つの出力（形状の予測テンソル`(10,)`）を持つモデルを見ました。では、複数の入力や出力を持つモデルではどうでしょう。\n",
        "\n",
        "次のモデルを考えてみましょう。形状 `(32, 32, 3)` の画像入力(`（高さ、幅、チャネル）`）、形状 `(None, 10)`の時系列入力（`(timesteps, features)`）があります。モデルは、これらの入力の組み合わせから2つの出力（「スコア」（形状`(1,)`）および5つのクラスにわたる確率分布（形状`(5,)`）を計算します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ntmviPVuPI7D"
      },
      "outputs": [],
      "source": [
        "image_input = keras.Input(shape=(32, 32, 3), name=\"img_input\")\n",
        "timeseries_input = keras.Input(shape=(None, 10), name=\"ts_input\")\n",
        "\n",
        "x1 = layers.Conv2D(3, 3)(image_input)\n",
        "x1 = layers.GlobalMaxPooling2D()(x1)\n",
        "\n",
        "x2 = layers.Conv1D(3, 3)(timeseries_input)\n",
        "x2 = layers.GlobalMaxPooling1D()(x2)\n",
        "\n",
        "x = layers.concatenate([x1, x2])\n",
        "\n",
        "score_output = layers.Dense(1, name=\"score_output\")(x)\n",
        "class_output = layers.Dense(5, activation=\"softmax\", name=\"class_output\")(x)\n",
        "\n",
        "model = keras.Model(\n",
        "    inputs=[image_input, timeseries_input], outputs=[score_output, class_output]\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BXeG97WpnPJr"
      },
      "source": [
        "ここで何が行われているか明確に分かるようにこのモデルをプロットしてみましょう（プロットに表示される形状は、サンプルごとの形状ではなく、バッチの形状であることに注意してください）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lzrnY3xo79iA"
      },
      "outputs": [],
      "source": [
        "keras.utils.plot_model(model, \"multi_input_and_output_model.png\", show_shapes=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nAXWeBnT0COk"
      },
      "source": [
        "コンパイル時に損失関数をリストとして渡すことにより出力ごとに異なる損失を指定できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Dd2abNMC7XrP"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "B6i258rpl0Lv"
      },
      "source": [
        "モデルに単一の損失関数のみを渡した場合、同じ損失関数がすべての出力に適用されます（ここでは適切ではありません）。\n",
        "\n",
        "同様にメトリックの場合："
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iTHQ2ULuVsQM"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],\n",
        "    metrics=[\n",
        "        [\n",
        "            keras.metrics.MeanAbsolutePercentageError(),\n",
        "            keras.metrics.MeanAbsoluteError(),\n",
        "        ],\n",
        "        [keras.metrics.CategoricalAccuracy()],\n",
        "    ],\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UsFMZWYxwqbM"
      },
      "source": [
        "出力レイヤーに名前を付けたので、dict を介して出力ごとの損失とメトリックを指定することもできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Pr9RN6wGr1FN"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss={\n",
        "        \"score_output\": keras.losses.MeanSquaredError(),\n",
        "        \"class_output\": keras.losses.CategoricalCrossentropy(),\n",
        "    },\n",
        "    metrics={\n",
        "        \"score_output\": [\n",
        "            keras.metrics.MeanAbsolutePercentageError(),\n",
        "            keras.metrics.MeanAbsoluteError(),\n",
        "        ],\n",
        "        \"class_output\": [keras.metrics.CategoricalAccuracy()],\n",
        "    },\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NTiNnyjiJe5P"
      },
      "source": [
        "3 つ以上の出力がある場合は、明示的な名前とディクショナリを使用することをお勧めします。\n",
        "\n",
        "`loss_weights`引数を使用すると、異なる出力固有の損失に異なる重みを与えることができます（この例でクラス損失の 2 倍の重要性を与えることにより、「スコア」損失を優先する場合など）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pg2puZ5KvBKw"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss={\n",
        "        \"score_output\": keras.losses.MeanSquaredError(),\n",
        "        \"class_output\": keras.losses.CategoricalCrossentropy(),\n",
        "    },\n",
        "    metrics={\n",
        "        \"score_output\": [\n",
        "            keras.metrics.MeanAbsolutePercentageError(),\n",
        "            keras.metrics.MeanAbsoluteError(),\n",
        "        ],\n",
        "        \"class_output\": [keras.metrics.CategoricalAccuracy()],\n",
        "    },\n",
        "    loss_weights={\"score_output\": 2.0, \"class_output\": 1.0},\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "k9MUn5WXtatT"
      },
      "source": [
        "これらの出力が予測用であり、トレーニング用ではない場合、特定の出力の損失を計算しないことを選択することもできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wq2Ecw9oeLVl"
      },
      "outputs": [],
      "source": [
        "# List loss version\n",
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss=[None, keras.losses.CategoricalCrossentropy()],\n",
        ")\n",
        "\n",
        "# Or dict loss version\n",
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss={\"class_output\": keras.losses.CategoricalCrossentropy()},\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZqsjpRqsnORf"
      },
      "source": [
        "fit でマルチ入力またはマルチ出力モデルにデータを渡すとコンパイルで損失関数を指定するのと同じように機能します。**NumPy 配列のリスト**（損失関数を受け取った出力に 1:1 でマッピング）または**出力の名前を NumPy 配列にマッピングする dict** を渡すことができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LIftd9mSB81g"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "    optimizer=keras.optimizers.RMSprop(1e-3),\n",
        "    loss=[keras.losses.MeanSquaredError(), keras.losses.CategoricalCrossentropy()],\n",
        ")\n",
        "\n",
        "# Generate dummy NumPy data\n",
        "img_data = np.random.random_sample(size=(100, 32, 32, 3))\n",
        "ts_data = np.random.random_sample(size=(100, 20, 10))\n",
        "score_targets = np.random.random_sample(size=(100, 1))\n",
        "class_targets = np.random.random_sample(size=(100, 5))\n",
        "\n",
        "# Fit on lists\n",
        "model.fit([img_data, ts_data], [score_targets, class_targets], batch_size=32, epochs=1)\n",
        "\n",
        "# Alternatively, fit on dicts\n",
        "model.fit(\n",
        "    {\"img_input\": img_data, \"ts_input\": ts_data},\n",
        "    {\"score_output\": score_targets, \"class_output\": class_targets},\n",
        "    batch_size=32,\n",
        "    epochs=1,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VNzcGRy1WiDc"
      },
      "source": [
        "以下は`Dataset`のユースケースです。NumPy 配列と同様に、`Dataset`はdicts のタプルを返します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4YVlRTcpwknb"
      },
      "outputs": [],
      "source": [
        "train_dataset = tf.data.Dataset.from_tensor_slices(\n",
        "    (\n",
        "        {\"img_input\": img_data, \"ts_input\": ts_data},\n",
        "        {\"score_output\": score_targets, \"class_output\": class_targets},\n",
        "    )\n",
        ")\n",
        "train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)\n",
        "\n",
        "model.fit(train_dataset, epochs=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "R4CsiI5H2HQ9"
      },
      "source": [
        "## コールバックの使用\n",
        "\n",
        "Keras のコールバックは、トレーニング中の異なる時点（エポックの始め、バッチの終わり、エポックの終わりなど）で呼び出されるオブジェクトで、以下のような動作を実装するために使用できます。\n",
        "\n",
        "- トレーニング中にさまざまな時点で検証を行う（組み込みのエポックごとの検証だけでなく）\n",
        "- 定期的に、または特定の精度しきい値を超えたときにモデルをチェックポイントする\n",
        "- 学習が停滞したときにモデルの学習率を変更する\n",
        "- 学習が停滞したときにトップレイヤーを微調整する\n",
        "- トレーニング終了時、または特定のパフォーマンスしきい値を超えたときにメールまたはインスタントメッセージ通知を送信する\n",
        "- その他\n",
        "\n",
        "コールバックは、リストとして`fit()`の呼び出しに渡すことができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kOArJW1vXhEl"
      },
      "outputs": [],
      "source": [
        "model = get_compiled_model()\n",
        "\n",
        "callbacks = [\n",
        "    keras.callbacks.EarlyStopping(\n",
        "        # Stop training when `val_loss` is no longer improving\n",
        "        monitor=\"val_loss\",\n",
        "        # \"no longer improving\" being defined as \"no better than 1e-2 less\"\n",
        "        min_delta=1e-2,\n",
        "        # \"no longer improving\" being further defined as \"for at least 2 epochs\"\n",
        "        patience=2,\n",
        "        verbose=1,\n",
        "    )\n",
        "]\n",
        "model.fit(\n",
        "    x_train,\n",
        "    y_train,\n",
        "    epochs=20,\n",
        "    batch_size=64,\n",
        "    callbacks=callbacks,\n",
        "    validation_split=0.2,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EGl2HS9QjLwu"
      },
      "source": [
        "### 数多くの組み込みコールバックが利用可能\n",
        "\n",
        "- `ModelCheckpoint`: 定期的にモデルを保存する\n",
        "- `EarlyStopping`: トレーニングによって検証指標が改善されなくなったら、トレーニングを停止する\n",
        "- `TensorBoard`: [TensorBoard](https://www.tensorflow.org/tensorboard) で視覚化できるモデルログを定期的に記述する（詳細については、「視覚化」セクションを参照）。\n",
        "- `CSVLogger`: 損失およびメトリックデータを CSV ファイルにストリーミング\n",
        "- その他\n",
        "\n",
        "完全なリストについては[コールバックのドキュメント](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/)を参照してください。\n",
        "\n",
        "### コールバックを記述する\n",
        "\n",
        "基底クラス`keras.callbacks.Callback`を拡張することにより、カスタムコールバックを作成できます。コールバックは、クラスプロパティ`self.model`を通じて関連するモデルにアクセスできます。\n",
        "\n",
        "詳細につきましては、[「カスタムコールバックの作成に関する完全なガイド」](https://www.tensorflow.org/guide/keras/custom_callback/)を参照してください。\n",
        "\n",
        "以下は、トレーニング時にバッチごとの損失値のリストを保存する簡単な例です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xbr9eYTtAfg9"
      },
      "outputs": [],
      "source": [
        "class LossHistory(keras.callbacks.Callback):\n",
        "    def on_train_begin(self, logs):\n",
        "        self.per_batch_losses = []\n",
        "\n",
        "    def on_batch_end(self, batch, logs):\n",
        "        self.per_batch_losses.append(logs.get(\"loss\"))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mfBuL1RfYWYu"
      },
      "source": [
        "## モデルのチェックポイント\n",
        "\n",
        "比較的大きなデータセットでモデルをトレーニングする場合、モデルのチェックポイントを頻繁に保存することが重要です。\n",
        "\n",
        "`ModelCheckpoint`コールバックを使用するのが最も簡単な方法です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZJGVnZDOgCGz"
      },
      "outputs": [],
      "source": [
        "model = get_compiled_model()\n",
        "\n",
        "callbacks = [\n",
        "    keras.callbacks.ModelCheckpoint(\n",
        "        # Path where to save the model\n",
        "        # The two parameters below mean that we will overwrite\n",
        "        # the current checkpoint if and only if\n",
        "        # the `val_loss` score has improved.\n",
        "        # The saved model name will include the current epoch.\n",
        "        filepath=\"mymodel_{epoch}\",\n",
        "        save_best_only=True,  # Only save a model if `val_loss` has improved.\n",
        "        monitor=\"val_loss\",\n",
        "        verbose=1,\n",
        "    )\n",
        "]\n",
        "model.fit(\n",
        "    x_train, y_train, epochs=2, batch_size=64, callbacks=callbacks, validation_split=0.2\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vlzo8T5ecYEa"
      },
      "source": [
        "`ModelCheckpoint`コールバックを使用するとフォールトトレランスを実装できます。フォールトトレランスはトレーニングがランダムに中断された場合に、モデルの最後に保存された状態からトレーニングを再開する機能です。 以下は基本的な例です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5lcqi3mWcaFK"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "\n",
        "# Prepare a directory to store all the checkpoints.\n",
        "checkpoint_dir = \"./ckpt\"\n",
        "if not os.path.exists(checkpoint_dir):\n",
        "    os.makedirs(checkpoint_dir)\n",
        "\n",
        "\n",
        "def make_or_restore_model():\n",
        "    # Either restore the latest model, or create a fresh one\n",
        "    # if there is no checkpoint available.\n",
        "    checkpoints = [checkpoint_dir + \"/\" + name for name in os.listdir(checkpoint_dir)]\n",
        "    if checkpoints:\n",
        "        latest_checkpoint = max(checkpoints, key=os.path.getctime)\n",
        "        print(\"Restoring from\", latest_checkpoint)\n",
        "        return keras.models.load_model(latest_checkpoint)\n",
        "    print(\"Creating a new model\")\n",
        "    return get_compiled_model()\n",
        "\n",
        "\n",
        "model = make_or_restore_model()\n",
        "callbacks = [\n",
        "    # This callback saves a SavedModel every 100 batches.\n",
        "    # We include the training loss in the saved model name.\n",
        "    keras.callbacks.ModelCheckpoint(\n",
        "        filepath=checkpoint_dir + \"/ckpt-loss={loss:.2f}\", save_freq=100\n",
        "    )\n",
        "]\n",
        "model.fit(x_train, y_train, epochs=1, callbacks=callbacks)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "svOMLNI3mjli"
      },
      "source": [
        "また、モデルを保存および復元するための独自のコールバックを記述することもできます。\n",
        "\n",
        "シリアル化と保存の完全なガイドについては、[「モデルの保存とシリアル化のガイド」](https://www.tensorflow.org/guide/keras/save_and_serialize/)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bg0pkgGN7FXf"
      },
      "source": [
        "## 学習率スケジューリングの使用\n",
        "\n",
        "ディープラーニングモデルをトレーニングするときの一般的なパターンは、トレーニングが進むにつれて徐々に学習を減らすことです。 これは一般に「学習率の減衰」として知られています。\n",
        "\n",
        "学習減衰スケジュールは、静的（その時点のエポックまたはその時点のバッチインデックスの関数として事前に指定）または動的（モデルの現在の動作、特に検証損失に対応）にすることができます。\n",
        "\n",
        "### オプティマイザにスケジュールを渡す\n",
        "\n",
        "オプティマイザの`learning_rate`引数としてスケジュールオブジェクトを渡すことで、静的学習率の減衰スケジュールを簡単に使用できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8o5pkAe1QTyy"
      },
      "outputs": [],
      "source": [
        "initial_learning_rate = 0.1\n",
        "lr_schedule = keras.optimizers.schedules.ExponentialDecay(\n",
        "    initial_learning_rate, decay_steps=100000, decay_rate=0.96, staircase=True\n",
        ")\n",
        "\n",
        "optimizer = keras.optimizers.RMSprop(learning_rate=lr_schedule)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mmYju7UaKXWT"
      },
      "source": [
        "`ExponentialDecay`、`PiecewiseConstantDecay`、`PolynomialDecay`、`InverseTimeDecay`などの組み込みスケジュールを利用できます。\n",
        "\n",
        "### コールバックを使用して動的学習率のスケジュールを実装\n",
        "\n",
        "オプティマイザは検証メトリックにアクセスできないため、これらのスケジュールオブジェクトでは動的学習率のスケジュールを実現できません。（検証の損失が改善されなくなったときに学習率を下げるなど）\n",
        "\n",
        "ただし、コールバックは、検証メトリックを含むすべてのメトリックにアクセスできます。このパターンでは、コールバックを使用してオプティマイザのその時点の学習率を変更します。これは`ReduceLROnPlateau`コールバックとして組み込まれています。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lOx7m1rNykV5"
      },
      "source": [
        "## トレーニング時の損失と測定基準の視覚化\n",
        "\n",
        "トレーニング時にモデルを監視する最善の方法は、[TensorBoard](https://www.tensorflow.org/tensorboard) を使用することです。これは、ローカルで実行できるブラウザベースのアプリケーションで、以下の機能を提供します。\n",
        "\n",
        "- トレーニングと評価のための損失とメトリクスのライブプロット\n",
        "- レイヤーアクティベーションのヒストグラムの視覚化（オプション）\n",
        "- `Embedding`レイヤーが学習した埋め込みスペースの 3D 視覚化（オプション）\n",
        "\n",
        "TensorFlow を pip でインストールした場合は、コマンドラインから TensorBoard を起動できます。\n",
        "\n",
        "```\n",
        "tensorboard --logdir=/full_path_to_your_logs\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3SMAHA0zgb4r"
      },
      "source": [
        "### TensorBoardコールバックの使用\n",
        "\n",
        "`TensorBoard`コールバックは、Keras モデルと fit メソッドで TensorBoard を使用する最も簡単な方法です。\n",
        "\n",
        "最も単純なケースでは、コールバックがログを書き込む場所を指定するだけです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "sJSgiVW4EVNA"
      },
      "outputs": [],
      "source": [
        "keras.callbacks.TensorBoard(\n",
        "    log_dir=\"/full_path_to_your_logs\",\n",
        "    histogram_freq=0,  # How often to log histogram visualizations\n",
        "    embeddings_freq=0,  # How often to log embedding visualizations\n",
        "    update_freq=\"epoch\",\n",
        ")  # How often to write logs (default: once per epoch)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Otu2i1gbeymg"
      },
      "source": [
        "詳細については、[`TensorBoard`コールバックのドキュメント](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks/tensorboard/)を参照してください。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "train_and_evaluate.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
